package com.gitee.wsl.ext.base

//package com.blankj.utilcode.util.ColorUtils;

import com.gitee.wsl.math.matrix.InvertibleMatrix.Companion.matrixMultiply
import com.gitee.wsl.math.Math.constrain
import kotlin.math.pow
import kotlin.random.Random


object ColorExt{
    const val ON_SURFACE_OPACITY_BUTTON_DISABLED: Float = 0.38f

    val SRGB_TO_XYZ = arrayOf(
        doubleArrayOf(0.41233895, 0.35762064, 0.18051042),
        doubleArrayOf(0.2126, 0.7152, 0.0722),
        doubleArrayOf(0.01932141, 0.11916382, 0.95034478)
    )

    val XYZ_TO_SRGB = arrayOf(
        doubleArrayOf(
            3.2413774792388685, -1.5376652402851851, -0.49885366846268053
        ), doubleArrayOf(
            -0.9691452513005321, 1.8758853451067872, 0.04156585616912061
        ), doubleArrayOf(
            0.05562093689691305, -0.20395524564742123, 1.0571799111220335
        )
    )

    val WHITE_POINT_D65 = doubleArrayOf(95.047, 100.0, 108.883)

    /** Converts a color from RGB components to ARGB format.  */
    fun argbFromRgb(red: Int, green: Int, blue: Int): Int {
        return 255 shl 24 or (red and 255 shl 16) or (green and 255 shl 8) or (blue and 255)
    }

    /** Returns the alpha component of a color in ARGB format.  */
    fun alphaFromArgb(argb: Int): Int {
        return argb shr 24 and 255
    }

    /** Returns the red component of a color in ARGB format.  */
    fun redFromArgb(argb: Int): Int {
        return argb shr 16 and 255
    }

    /** Returns the green component of a color in ARGB format.  */
    fun greenFromArgb(argb: Int): Int {
        return argb shr 8 and 255
    }

    /** Returns the blue component of a color in ARGB format.  */
    fun blueFromArgb(argb: Int): Int {
        return argb and 255
    }

    fun toArgb(argb: Int):String{
        val a= alphaFromArgb(argb)
        val r= redFromArgb(argb)
        val g= greenFromArgb(argb)
        val b= blueFromArgb(argb)
        return "rgba($r,$g,$b,$a)"
    }

    fun toRgb(argb: Int):String{
        val r= redFromArgb(argb)
        val g= greenFromArgb(argb)
        val b= blueFromArgb(argb)
        return "rgb($r,$g,$b)"
    }

    /** Returns whether a color in ARGB format is opaque.  */
    fun isOpaque(argb: Int): Boolean {
        return alphaFromArgb(argb) >= 255
    }

    /** Converts a color from ARGB to XYZ.  */
    fun argbFromXyz(x: Double, y: Double, z: Double): Int {
        val matrix: Array<DoubleArray> = XYZ_TO_SRGB
        val linearR = matrix[0][0] * x + matrix[0][1] * y + matrix[0][2] * z
        val linearG = matrix[1][0] * x + matrix[1][1] * y + matrix[1][2] * z
        val linearB = matrix[2][0] * x + matrix[2][1] * y + matrix[2][2] * z
        val r: Int = delinearized(linearR)
        val g: Int = delinearized(linearG)
        val b: Int = delinearized(linearB)
        return argbFromRgb(r, g, b)
    }

    /** Converts a color from XYZ to ARGB.  */
    fun xyzFromArgb(argb: Int): DoubleArray {
        val r: Double = linearized(redFromArgb(argb))
        val g: Double = linearized(greenFromArgb(argb))
        val b: Double = linearized(blueFromArgb(argb))
        return matrixMultiply(doubleArrayOf(r, g, b), SRGB_TO_XYZ)
    }

    /** Converts a color represented in Lab color space into an ARGB integer.  */
    fun argbFromLab(l: Double, a: Double, b: Double): Int {
        val whitePoint: DoubleArray =WHITE_POINT_D65
        val fy = (l + 16.0) / 116.0
        val fx = a / 500.0 + fy
        val fz = fy - b / 200.0
        val xNormalized: Double = labInvf(fx)
        val yNormalized: Double = labInvf(fy)
        val zNormalized: Double = labInvf(fz)
        val x = xNormalized * whitePoint[0]
        val y = yNormalized * whitePoint[1]
        val z = zNormalized * whitePoint[2]
        return argbFromXyz(x, y, z)
    }

    /**
     * Converts a color from ARGB representation to L*a*b* representation.
     *
     * @param argb the ARGB representation of a color
     * @return a Lab object representing the color
     */
    fun labFromArgb(argb: Int): DoubleArray {
        val linearR: Double = linearized(redFromArgb(argb))
        val linearG: Double = linearized(greenFromArgb(argb))
        val linearB: Double = linearized(blueFromArgb(argb))
        val matrix: Array<DoubleArray> = SRGB_TO_XYZ
        val x = matrix[0][0] * linearR + matrix[0][1] * linearG + matrix[0][2] * linearB
        val y = matrix[1][0] * linearR + matrix[1][1] * linearG + matrix[1][2] * linearB
        val z = matrix[2][0] * linearR + matrix[2][1] * linearG + matrix[2][2] * linearB
        val whitePoint: DoubleArray = WHITE_POINT_D65
        val xNormalized = x / whitePoint[0]
        val yNormalized = y / whitePoint[1]
        val zNormalized = z / whitePoint[2]
        val fx: Double = labF(xNormalized)
        val fy: Double = labF(yNormalized)
        val fz: Double = labF(zNormalized)
        val l = 116.0 * fy - 16
        val a = 500.0 * (fx - fy)
        val b = 200.0 * (fy - fz)
        return doubleArrayOf(l, a, b)
    }

    /**
     * Converts an L* value to an ARGB representation.
     *
     * @param lstar L* in L*a*b*
     * @return ARGB representation of grayscale color with lightness matching L*
     */
    fun argbFromLstar(lstar: Double): Int {
        val y = yFromLstar(lstar)
        val component: Int = delinearized(y)
        return argbFromRgb(component, component, component)
    }

    /**
     * Computes the L* value of a color in ARGB representation.
     *
     * @param argb ARGB representation of a color
     * @return L*, from L*a*b*, coordinate of the color
     */
    fun lstarFromArgb(argb: Int): Double {
        val y = xyzFromArgb(argb)[1]
        return 116.0 * labF(y / 100.0) - 16.0
    }

    /**
     * Converts an L* value to a Y value.
     *
     *
     * L* in L*a*b* and Y in XYZ measure the same quantity, luminance.
     *
     *
     * L* measures perceptual luminance, a linear scale. Y in XYZ measures relative luminance, a
     * logarithmic scale.
     *
     * @param lstar L* in L*a*b*
     * @return Y in XYZ
     */
    fun yFromLstar(lstar: Double): Double {
        return 100.0 * labInvf((lstar + 16.0) / 116.0)
    }

    /**
     * Converts a Y value to an L* value.
     *
     *
     * L* in L*a*b* and Y in XYZ measure the same quantity, luminance.
     *
     *
     * L* measures perceptual luminance, a linear scale. Y in XYZ measures relative luminance, a
     * logarithmic scale.
     *
     * @param y Y in XYZ
     * @return L* in L*a*b*
     */
    fun lstarFromY(y: Double): Double {
        return labF(y / 100.0) * 116.0 - 16.0
    }

    /**
     * Linearizes an RGB component.
     *
     * @param rgbComponent 0 <= rgb_component <= 255, represents R/G/B channel
     * @return 0.0 <= output <= 100.0, color channel converted to linear RGB space
     */
    fun linearized(rgbComponent: Int): Double {
        val normalized = rgbComponent / 255.0
        return if (normalized <= 0.040449936) {
            normalized / 12.92 * 100.0
        } else {
            ((normalized + 0.055) / 1.055).pow(2.4) * 100.0
        }
    }

    /**
     * Delinearizes an RGB component.
     *
     * @param rgbComponent 0.0 <= rgb_component <= 100.0, represents linear R/G/B channel
     * @return 0 <= output <= 255, color channel converted to regular RGB space
     */
    fun delinearized(rgbComponent: Double): Int {
        val normalized = rgbComponent / 100.0
        var delinearized = 0.0
        delinearized = if (normalized <= 0.0031308) {
            normalized * 12.92
        } else {
            1.055 * normalized.pow(1.0 / 2.4) - 0.055
        }
        return constrain((delinearized * 255.0).toFloat(),0f, 255f).toInt()
    }

    /**
     * Returns the standard white point; white on a sunny day.
     *
     * @return The white point
     */
    fun whitePointD65(): DoubleArray? {
        return WHITE_POINT_D65
    }

    fun labF(t: Double): Double {
        val e = 216.0 / 24389.0
        val kappa = 24389.0 / 27.0
        return if (t > e) {
            t.pow(1.0 / 3.0)
        } else {
            (kappa * t + 16) / 116
        }
    }

    fun labInvf(ft: Double): Double {
        val e = 216.0 / 24389.0
        val kappa = 24389.0 / 27.0
        val ft3 = ft * ft * ft
        return if (ft3 > e) {
            ft3
        } else {
            (116 * ft - 16) / kappa
        }
    }

    /**
     * Color-int to color-string.
     *
     * @param colorInt The color-int.
     * @return color-string
     */
    @OptIn(ExperimentalStdlibApi::class)
    fun int2RgbString(colorInt: Int): String {
        var colorInt = colorInt
        colorInt = colorInt and 0x00ffffff
        var color = colorInt.toHexString()
        while (color.length < 6) {
            color = "0$color"
        }
        return "#$color"
    }

    /**
     * Color-int to color-string.
     *
     * @param colorInt The color-int.
     * @return color-string
     */
    @OptIn(ExperimentalStdlibApi::class)
    fun int2ArgbString(colorInt: Int): String {
        var color = colorInt.toHexString()
        while (color.length < 6) {
            color = "0$color"
        }
        while (color.length < 8) {
            color = "f$color"
        }
        return "#$color"
    }


    /**
     * Return the random color.
     *
     * @return the random color
     */
    fun getRandomColor(): Int {
        return getRandomColor(true)
    }


    /**
     * 返回一个颜色 ARGB 色值数组 ( 返回十进制 )
     * @param color argb/rgb color
     * @return ARGB 色值数组 { alpha, red, green, blue }
     */
    fun getARGB(color: Int): IntArray {
        val argb = IntArray(4)
        val alpha = color.alpha
        if(alpha in 0..255)
            argb[0] = alpha
        else
            argb[0] = 255
        argb[1] = color.red
        argb[2] = color.green
        argb[3] = color.blue
        return argb
    }

    /**
     * 获取灰度值
     * @param color int color
     * @return 灰度值
     */
    fun grayLevel(color: Int): Int {
        // [] { alpha, red, green, blue }
        val argb: IntArray = getARGB(color)
        return (argb[1] * 0.299f + argb[2] * 0.587f + argb[3] * 0.114f).toInt()
    }


    /**
     * Return the random color.
     *
     * @param supportAlpha True to support alpha, false otherwise.
     * @return the random color
     */
    fun getRandomColor(supportAlpha: Boolean): Int {
        val high = if (supportAlpha) (Random.nextInt() * 0x100) shl 24 else -0x1000000
        return high or (Random.nextInt() * 0x1000000)
    }


    fun colorPart(c: Int, shift: Int) = (c and (0xFF shl shift)) shr shift

    fun colorTo(from: Int, to: Int, factor: Float): Int {
        val factor = 1f.coerceAtMost(factor.coerceAtLeast(0f))
        val a1 = (from and (0xFF shl 24)) shr 24
        val r1 = (from and (0xFF shl 16)) shr 16
        val g1 = (from and (0xFF shl 8)) shr 8
        val b1 = (from and 0xFF)
        val a2 = (to and (0xFF shl 24)) shr 24
        val r2 = (to and (0xFF shl 16)) shr 16
        val g2 = (to and (0xFF shl 8)) shr 8
        val b2 = (to and 0xFF)
        val a = (if (a1 > a2) a1 - (a1 - a2) * factor else (a2 - a1) * factor + a1).toInt()
        val r = (if (r1 > r2) r1 - (r1 - r2) * factor else (r2 - r1) * factor + r1).toInt()
        val g = (if (g1 > g2) g1 - (g1 - g2) * factor else (g2 - g1) * factor + g1).toInt()
        val b = (if (b1 > b2) b1 - (b1 - b2) * factor else (b2 - b1) * factor + b1).toInt()

        return (a shl 24) + (r shl 16) + (g shl 8) + b
    }


}

/*fun Int.toColor(): String {
    return String.format("#%08X", this)
}

val Int.HEX:String
    get() =  String.format("#%08X", this)
val Long.HEX:String
   get() =  String.format("#%08X", this)*/
val Int.RGBA:String
    get() =  ColorExt.toArgb(this)
val Long.RGBA:String
    get() =  ColorExt.toArgb(this.toInt())
val Int.RGB:String
    get() =  ColorExt.toRgb(this)
val Long.RGB:String
    get() =  ColorExt.toRgb(this.toInt())

fun Int.partColor(shift: Int) =
    (this and (0xFF shl shift)) shr shift

fun Int.setAlpha(value: Int) =
    (value shl 24) + (this and 0xffffff)
fun Int.setRed(value: Int) =
    (value shl 16) + (this and 0xff00ffff.toInt())
fun Int.setGreen(value: Int) =
    (value shl 8) + (this and 0xffff00ff.toInt())
fun Int.setBlue(value: Int) =
    value + (this and 0xffffff00.toInt())

val Int.alpha
    get() = partColor(24)
val Int.red
    get() = partColor(16)
val Int.green
    get() = partColor(8)
val Int.blue
    get() = partColor(0)

data class HSVSpec(
    val h: Float,
    val s: Float,
    val v: Float
)


